/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

#include <sys/stat.h>
#include <dirent.h> /* Relies on MinGW / Unix-like systems */

#include "ilua.h"
#include "lib_insim.h"
#include "events.h"
#include "lpack.h"
#include "miscfuncs.h"

#ifndef WIN32
#define MAX_PATH 256
#endif

extern void *I;

int ilua_sendmsg(lua_State *L)
{
    int n = lua_gettop(L);
    if ((n > 2) || (!lua_isstring(L, 1)))
    {
        lua_pushstring(L, "incorrect arguments");
        lua_error(L);
    }

    insim_send((struct insim_t *)I, lua_tostring(L, 1), lua_tonumber(L, 2));
    return 0;
}

int ilua_quit(lua_State *L)
{
    struct insim_t *t = (struct insim_t *)I;
    t->internals.quit = TRUE;
    return 0;
}

int ilua_rehash(lua_State *L)
{
    ilua_evt_fire(L, EVT_SHUTDOWN, NULL, 0);
    lua_gc(L, LUA_GCCOLLECT, 0);
    ilua_evt_fire(L, EVT_CLEAR_BINDS, NULL, 0);

	ilua_load(L);

    load_conf(I, L);
    return 0;
}

int ilua_sleep(lua_State *L)
{
    int n = lua_gettop(L);
    if ((n > 1) || (!lua_isnumber(L, 1)))
    {
        lua_pushstring(L, "incorrect arguments");
        lua_error(L);
    }

    LSLEEP(lua_tonumber(L, 1));
    return 0;
}

int ilua_version(lua_State *L)
{
    lua_pushnumber(L, VERSION_NUM);
    return 1;
}

int ilua_isverbose(lua_State *L)
{
    struct insim_t *t = (struct insim_t *)I;
    lua_pushnumber(L, t->internals.verbose);
    return 1;
}


int ilua_mstime (lua_State *L) {
  ULONGLONG t;
//  if (lua_isnoneornil(L, 1)) { /* called without args? */

    SYSTEMTIME systemTime;
    GetSystemTime( &systemTime );

    FILETIME fileTime;
    SystemTimeToFileTime( &systemTime, &fileTime );

    ULARGE_INTEGER uli;
    uli.LowPart = fileTime.dwLowDateTime; // could use memcpy here!
    uli.HighPart = fileTime.dwHighDateTime;
    t = uli.QuadPart/10000;

    lua_pushnumber(L, (lua_Number)t);
//  } else {
//    lua_pushnil(L);
//  }
  return 1;
}


const luaL_reg luaLFS_lib[] =
{
    {"sendmsg", ilua_sendmsg},
    {"quit", ilua_quit},
    {"rehash", ilua_rehash},
    {"sleep", ilua_sleep},
    {"version", ilua_version},
    {"verbose", ilua_isverbose},
    {"mstime", ilua_mstime},
    {NULL, NULL}
};

void luareg_luaLFS(lua_State *L)
{
    luaL_register(L, "luaLFS", luaLFS_lib);
    return;
}

lua_State *ilua_init()
{
	/* initialize Lua */
	lua_State *L = lua_open();

	/* load Lua base libraries */
	luaL_openlibs(L);
    luaopen_pack(L);

	/* register any globals and definitions */
	lua_pushstring(L, "luaLFS_VERSION");
	lua_pushstring(L, VERSION);
	lua_settable(L, LUA_GLOBALSINDEX);

    luareg_luaLFS(L);
	luaopen_bit(L);

	return L;
}

int ilua_close(lua_State *L)
{
	ilua_evt_fire(L, EVT_SHUTDOWN, NULL, 0);
	/* cleanup Lua */
	if (L)
		lua_close(L);

	L = NULL;

	return 0;
}

int ilua_load(lua_State *L)
{
	ilua_load_core(L);
	ilua_load_custom(L);
	ilua_evt_fire(L, EVT_STARTUP, NULL, 0);

	return 1;
}

int ilua_load_core(lua_State *L)
{
    printf("Loading core\n");
	if (luaL_dofile(L, "scripts/core/core.lua") > 0)
	{
        fprintf(stderr, "Core not OK (%s)\n", lua_error(L));
        return -1;
	}

	return 0;
}

int ilua_load_custom(lua_State *L)
{
	int ret = 0;
	DIR *d = NULL;
	struct stat st;
	struct dirent *e = NULL;
	char b[MAX_PATH];
	memset(b, 0, MAX_PATH);

	d = opendir("scripts");
	if (d != NULL)
	{
		for (e = readdir(d); e; e = readdir(d))
		{
		    // XXX: This sucks huge cock, but it's fast
			if ((*(e->d_name) == '.') || (strstr(e->d_name, ".lua") == NULL))
				continue;

			memset(b, 0, sizeof(b));
			snprintf(b, sizeof(b) - 1, "scripts/%s", e->d_name);

			memset(&st, 0, sizeof(st));

			ret = stat(b, &st);
			if (ret == -1)
			{
				fprintf(stderr, "ilua_load_custom: stat on '%s' failed\n", b);
				continue;
			}

			/* We don't care about subdirectories. Sorry */
			if ((S_ISDIR(st.st_mode)))
				continue;

			printf("Loading %s\n", b);
			if (luaL_dofile(L, b))
				fprintf(stderr, "ilua_load_custom: luaL_dofile failed: '%s'\n", lua_tostring(L, -1));
		}
		closedir(d);
	}

	return 0;
}

int ilua_evt_fire(lua_State *L, char type, const char *arg, size_t length)
{
    if (L != NULL)
    {
        int n = 1, ret = 0;
        lua_getglobal(L, "evt_fire");
        lua_pushnumber(L, (int)type);
        if ((arg != NULL) && (length > 0))
        {
            lua_pushlstring(L, arg, length);
            n = 2;
        }
        ret = lua_pcall(L, n, 0, 0);
        if (ret)
        {
            fprintf(stderr, "ilua_evt_fire: lua_pcall failed calling '%d': '%s'\n", type, lua_tostring(L, -1));
            return -1;
        }
    }
    else
    {
        printf("BUG: Lua instance NULL!!\n");
        return -1;
    }
    return 0;
}
